package top.lishuoboy.regex.java;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.TimeInterval;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Console;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.*;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.nio.ByteOrder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Set;
import java.util.SortedMap;
import java.util.concurrent.ThreadPoolExecutor;

@Slf4j
public class MyCharsetPrint {
    static TimeInterval timer = DateUtil.timer();
    static ThreadPoolExecutor pool = ThreadUtil.newExecutor(100, 100, Integer.MAX_VALUE);
    static String BASE_DIR = "asset/0.charset";
    static Charset CHARSET_US_ASCII = StandardCharsets.US_ASCII;
    static Charset CHARSET_ISO_8859_1 = StandardCharsets.ISO_8859_1;
    static Charset CHARSET_UTF_16 = StandardCharsets.UTF_16;
    static Charset CHARSET_UTF_8 = StandardCharsets.UTF_8;
    static Charset CHARSET_GB2312 = Charset.forName("GB2312");      // 【停更】1980年发布GB2312。详见GB18030的注释
    static Charset CHARSET_GBK = Charset.forName("GBK");            // 【停更】1995年发布GBK。详见GB18030的注释
    /**
     * # GB2312、GBK、GB18030 版本说明，以及JDK增强支持GB18030说明
     * 1. 【停更】1980年发布GB2312
     * 2. 【停更】1995年发布GBK，取代GB2312
     * 3. 【更新】2000年发布GB18030,取代GBK。GB18030目前发布2000、2005、2022三个版本，
     * 4. jdk17.0.8、jdk11.0.20 和 jdk8u381 对GB18030编码提供了增强支持。 默认版本从GB18030-2000升级到了18030-2022。如需使用GB18030-2000，需指定虚拟机参数-Djdk.charset.GB18030=2000。详见 <https://blog.csdn.net/breeze915/article/details/140686613>
     */
    static Charset CHARSET_GB18030 = Charset.forName("GB18030");

    public static void main(String[] args) {
        // 输出所有支持的字符集 及 对应的别名
        printAvailableCharsets();
        // 输出 '中' 的UTF-16（unicode）编码，正则找中文就是一以UTF-16为准
        System.err.println(Integer.toHexString('中'));   // 4e2d

        timer.restart();
        // 输出各个字符集的所有字符
        printChar("0.1-1.1", CHARSET_US_ASCII, false);
        printChar("0.1-1.1", CHARSET_US_ASCII, true);
        printChar("0.2-1.2", CHARSET_ISO_8859_1, false);
        printChar("0.2-1.2", CHARSET_ISO_8859_1, true);
        printChar("0.3-2.1", CHARSET_UTF_16, false);
        printChar("0.3-2.1", CHARSET_UTF_16, true);
        printChar("0.4-2.2", CHARSET_UTF_8, false);
        printChar("0.4-2.2", CHARSET_UTF_8, true);
        printChar("0.5-3.1", CHARSET_GB2312, false);
        printChar("0.5-3.1", CHARSET_GB2312, true);
        printChar("0.6-3.2", CHARSET_GBK, false);
        printChar("0.6-3.2", CHARSET_GBK, true);
        printChar("0.7-3.3", CHARSET_GB18030, false);
        printChar("0.7-3.3", CHARSET_GB18030, true);

        pool.shutdown();
        while (!pool.isTerminated()) {
            log.warn("pool.ActiveCount=={}", pool.getActiveCount());
            ThreadUtil.sleep(10000);
        }
        log.info("用时=={}", timer.intervalPretty());
    }

    /**
     * 输出所有字符
     *
     * @param isMarkDown 是否markdown格式
     */
    private static void printChar(String sortNo, Charset charset, boolean isMarkDown) {
        pool.submit(() -> {
            log.warn(StrUtil.fillAfter("name==" + charset, ' ', 30) + ", aliases=={}", charset.aliases());
            File file;
            if (isMarkDown) {
                file = new File(BASE_DIR, sortNo + "." + getCharsetNameWithYear(charset) + ".md");
            } else {
                file = new File(BASE_DIR, sortNo + "." + getCharsetNameWithYear(charset) + ".txt");
            }
            FileUtil.del(file);   // 删文件
            StringBuilder sbLine = StrUtil.builder();
            StringBuilder sbLines = StrUtil.builder();
            if (isMarkDown) {
                sbLines.append("""
                    |-0.5byte| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | a | b | c | d | e | f |
                    |-------:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
                      """);
            }
            /*
             US-ASCII/ISO_8859_1       占1              个字节。
             GB2312/GBK             占1/2            个字节。汉字   占2个字节
             GB18030                占1/2/4          个字节。汉字主要占2个字节
             UTF-16                 占  2/4          个字节。汉字   占2个字节。暂没找到3个字节以上的字符，不输出了
             UTF-8                  占1/2/3/4/5/6    个字节。汉字   占3个字节。暂没找到3个字节以上的字符，不输出了
             */
            long iMax;
            if (StrUtil.equalsAnyIgnoreCase(charset.name(), "US-ASCII", "ISO-8859-1")) {
                iMax = 1L << 8;
            } else if (StrUtil.equalsAnyIgnoreCase(charset.name(), "GB2312", "GBK")) {
                iMax = 1L << 16;
            } else if (StrUtil.equalsAnyIgnoreCase(charset.name(), "UTF-16", "GB18030")) {
                iMax = 1L << 32;
            } else if (StrUtil.equalsAnyIgnoreCase(charset.name(), "UTF-8")) {
                iMax = 1L << 32;
            } else {
                iMax = 1L;
                log.warn("字符集的长度未指定, charset==[{}]", charset);
            }

            // 实测字符范围
            long minVal = 1L << 24;
            long maxVal = 1L << 32;
            if (charset.equals(CHARSET_GB18030)) {   // 0x81308130L~0x8431a43fL, 0x90308130L~0x9939f73fL
                minVal = 0x81308130L;
                maxVal = 0x9939f73fL;
            } else if (charset.equals(CHARSET_UTF_16)) {
                minVal = 0xd800dc00L;
                maxVal = 0xd87adfefL;
            } else if (charset.equals(CHARSET_UTF_8)) {
                minVal = 0xf0908080L;
                maxVal = 0xf0aeafafL;
            }
            for (long i = 0; i < iMax; i++) {
                if (i >= 1 << 24 && !(i >= minVal && i <= maxVal)) {
                    continue;
                }
                byte[] bytes = ByteUtil.longToBytes(i, ByteOrder.BIG_ENDIAN);
                int maxLen;  // 转16进制的最大长度
                if (charset.equals(CHARSET_UTF_16)) {
                    maxLen = 4 * ((Long.toBinaryString(i).length() - 1) / 16 + 1);
                    bytes = ArrayUtil.sub(bytes, bytes.length - maxLen / 2, bytes.length);    // 截取后2/4个字节
                } else {
                    maxLen = 2 * ((Long.toBinaryString(i).length() - 1) / 8 + 1);
                    bytes = removeBefore0(bytes);
                }
                String idxStr = StrUtil.fillBefore(Long.toHexString(i), '0', maxLen);    // 总次数-16进制格式
                // 拼接行头
                if (i % 16 == 0) {
                    String linePre = StrUtil.format("|{}|", idxStr);
                    if (isMarkDown) {
                        linePre = StrUtil.format("|{}|", StrUtil.fillBefore(idxStr, ' ', 8));
                    }
                    sbLine.append(linePre);
                }
                String charStr;         // 最终字符
                charStr = new String(bytes, charset);
                // 多字节的字符又不是1个字符或者为"�"则置空
                if ((StrUtil.equalsAny(charStr, "�") || charStr.codePointCount(0, charStr.length()) != 1)
//                && !charset.equals(CHARSET_UTF_16)
                ) {
                    charStr = "";
                }
                // 转义几个字符
                if (charStr.equals("\b")) {
                    charStr = "\\b";        // BS
                } else if (charStr.equals("\n")) {
                    charStr = "\\n";        // LF
                } else if (charStr.equals("\r")) {
                    charStr = "\\r";        // CR
                } else if (charStr.equals("|") && isMarkDown) {
                    charStr = "&#124;";     // | 转义markdown分隔符
                }
                // 拼接行
                String lineText = StrUtil.format("{}|", charStr);
                if (isMarkDown) {
                    lineText = StrUtil.format(" {} |", charStr);
                }
                sbLine.append(lineText);
                // 拼接行尾
                if ((i + 1) % 16 == 0) {
                    sbLine.append("\r\n");
                    if (StrUtil.containsAny(sbLine, "||||||||||||||||", "|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |")) {  // 全行都为空则全行置为空
                        sbLine = StrUtil.builder();
                    }
                    sbLines.append(sbLine);
                    sbLine = StrUtil.builder();
                }
                // 写文件
                if ((i + 1) % (1 << 24) == 0) {
                    log.info(idxStr + "@" + file.getName());
//                    System.out.print(sbLines);
                    FileUtil.appendUtf8String(sbLines.toString(), file);
                    sbLines = StrUtil.builder();
                }
            }
//            System.out.print(sbLines);
            FileUtil.appendUtf8String(sbLines.toString(), file);
        });
    }

    /** 将字节数组中前面的0干掉（知道第一个不是0的结束） */
    private static byte[] removeBefore0(byte[] bytes) {
        byte[] bytesNew = new byte[1];
        for (int i = 0; i < bytes.length; i++) {
            if (bytes[i] != 0) {
                bytesNew = ArrayUtil.sub(bytes, i, bytes.length);
                return bytesNew;
            }
        }
        return bytesNew;
    }

    /** 输出所有支持的字符集 及 对应的别名 */
    private static void printAvailableCharsets() {
        SortedMap<String, Charset> availableCharsets = Charset.availableCharsets();
        for (Charset charset : availableCharsets.values()) {
            Console.log(StrUtil.fillAfter("name==" + charset, ' ', 30) + ", aliases=={}", charset.aliases());
        }
    }

    /** 获取字符集版本年份（主要用于中文在更新的GB18030） */
    private static Object getCharsetNameWithYear(Charset charset) {
        if (!charset.equals(CHARSET_GB18030)) {
            return charset;
        }
        String charsetName = charset.name();
        Set<String> aliases = charset.aliases();
        for (String alias : aliases) {
            if (StrUtil.startWithIgnoreCase(alias, charsetName + "-") && alias.length() == 5 + charsetName.length()) {
                return alias;
            }
        }
        return charset;
    }
}
